//=============================================================================
//                                                                            
//   Exercise code for the lecture
//                                                                            
//=============================================================================
//=============================================================================
//
//  CLASS ProceduralViewer - IMPLEMENTATION
//
//=============================================================================

//== INCLUDES =================================================================

#include "ProceduralViewer.h"
#include "../../utils/Mesh3DReader.h"

//== IMPLEMENTATION ========================================================== 

//=========================================================================================
// Perlin Noise Data
//=========================================================================================

// permutation table
static unsigned char permutation[] = { 151,160,137,91,90,15,
	131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
	190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
	88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
	77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
	102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
	135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
	5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
	223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
	129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
	251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
	49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
	138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
};


// gradients for 3d noise
static float g2[] = {
    1,2,
    1,-2,
    -1,2,
	-1,-2,
	2,1,
	2,-1,
	-2,1,
	-2,-1
};

// gradients for 3d noise
static float g3[] = {
    1,1,0,
    -1,1,0,
    1,-1,0,
    -1,-1,0,
    1,0,1,
    -1,0,1,
    1,0,-1,
    -1,0,-1, 
    0,1,1,
    0,-1,1,
    0,1,-1,
    0,-1,-1,
    1,1,0,
    0,-1,1,
    -1,1,0,
    0,-1,-1,
};

// gradients for 4D noise
static float g4[] = {
	0, -1, -1, -1,
	0, -1, -1, 1,
	0, -1, 1, -1,
	0, -1, 1, 1,
	0, 1, -1, -1,
	0, 1, -1, 1,
	0, 1, 1, -1,
	0, 1, 1, 1,
	-1, -1, 0, -1,
	-1, 1, 0, -1,
	1, -1, 0, -1,
	1, 1, 0, -1,
	-1, -1, 0, 1,
	-1, 1, 0, 1,
	1, -1, 0, 1,
	1, 1, 0, 1,
	
	-1, 0, -1, -1,
	1, 0, -1, -1,
	-1, 0, -1, 1,
	1, 0, -1, 1,
	-1, 0, 1, -1,
	1, 0, 1, -1,
	-1, 0, 1, 1,
	1, 0, 1, 1,
	0, -1, -1, 0,
	0, -1, -1, 0,
	0, -1, 1, 0,
	0, -1, 1, 0,
	0, 1, -1, 0,
	0, 1, -1, 0,
	0, 1, 1, 0,
	0, 1, 1, 0,
};



//=========================================================================================
// Color Maps
//=========================================================================================
#define PALE_BLUE	Vector3(0.25,0.25,0.35)
#define MEDIUM_BLUE	Vector3(0.10,0.10,0.30)
#define DARK_BLUE	Vector3(0.05,0.05,0.26)
#define DARKER_BLUE Vector3(0.03,0.03,0.20)
static Vector3 marble_color_map[] = {
	PALE_BLUE, PALE_BLUE,
	MEDIUM_BLUE, MEDIUM_BLUE, MEDIUM_BLUE, 
	PALE_BLUE, PALE_BLUE,
	DARK_BLUE, DARK_BLUE,
	DARKER_BLUE, DARKER_BLUE,
	PALE_BLUE, DARKER_BLUE
};

#define MEDIUM_RED		Vector3(0.50,0.05,0.05)
#define MEDIUM_YELLOW	Vector3(0.50,0.50,0.10)
static Vector3 red_yellow_color_map[] = {
	MEDIUM_RED, MEDIUM_RED,
	MEDIUM_YELLOW, MEDIUM_YELLOW
};

//-----------------------------------------------------------------------------

ProceduralViewer::
ProceduralViewer(const char* _title, int _width, int _height)
: TrackballViewer(_title, _width, _height)
{
  init();
}


//-----------------------------------------------------------------------------


void 
ProceduralViewer::
init()
{
  // initialize parent
  TrackballViewer::init();

  // set camera to look at world coordinate center
  set_scene_pos(Vector3(0.0, 0.0, 0.0), 2.0);
	
	
  // load mesh shader
	m_proceduralShader.create("procedural.vs", "procedural.fs");
	
	// load permutation table
	m_permutationTable.create(256,1,GL_RED, GL_RED, GL_UNSIGNED_BYTE, permutation);
	// make sure wrapping is correct
	m_permutationTable.bind();
	glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	m_permutationTable.unbind();
	
	// load 2D gradient table
	for(int i = 0; i < 8*2; i++) g2[i] /= sqrt(5.0);
	m_gradient2DTable.create(8,1,GL_RG32F, GL_RG, GL_FLOAT, g2);
	m_gradient2DTable.bind();
	glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	m_gradient2DTable.unbind();
	
	// load 3D gradient table
	for(int i = 0; i < 16*3; i++) g3[i] /= sqrt(2.0);
	m_gradient3DTable.create(16,1,GL_RGB32F_ARB, GL_RGB, GL_FLOAT, g3);
	m_gradient3DTable.bind();
	glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	m_gradient3DTable.unbind();
	
	// load 4D gradient table
	for(int i = 0; i < 32*4; i++) g4[i] /= sqrt(3.0);
	m_gradient4DTable.create(32,1,GL_RGBA32F_ARB, GL_RGBA, GL_FLOAT, g4);
	m_gradient4DTable.bind();
	glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	m_gradient4DTable.unbind();
	
	// color maps
	//std::vector<float> colorMap = createColorMapTexture(13, marble_color_map, 256);
	std::vector<float> colorMap = createColorMapTexture(4, red_yellow_color_map, 256);
	assert(colorMap.size() == 256*3);
	m_colorMap.create(colorMap.size()/3,1,GL_RGB,GL_RGB, GL_FLOAT, &(colorMap[0]) );
}
								
							  
//-----------------------------------------------------------------------------
void
ProceduralViewer::
loadMesh(const std::string& filenameObj )
{
	// load mesh from obj
	Mesh3DReader::read( filenameObj, m_mesh );

	// calculate normals
	if(!m_mesh.hasNormals())
		m_mesh.calculateVertexNormals();
	
	// get bounding box & reset scene camera accordingly
	Vector3 bbmin, bbmax;
	m_mesh.calculateBoundingBox(bbmin, bbmax);
	
	double radius = 0.5*(bbmin - bbmax).length();
	Vector3 center = 0.5*(bbmin + bbmax);
	
	set_scene_pos(center, radius);
	
	
}

//-----------------------------------------------------------------------------


void
ProceduralViewer::
keyboard(int key, int x, int y)
{
	switch (key)
	{			
		case 'h':
			printf("Help:\n");
			printf("'h'\t-\thelp\n");
			break;
		default:
			TrackballViewer::special(key, x, y);
			break;
	}
	
	glutPostRedisplay();
}

//-----------------------------------------------------------------------------


void
ProceduralViewer::
special(int key, int x, int y)
{
	switch (key)
	{			
		default:
			TrackballViewer::special(key, x, y);
			break;
	}
	
	glutPostRedisplay();
}


//-----------------------------------------------------------------------------


void 
ProceduralViewer::
draw_scene(DrawMode _draw_mode)
{
	// clear screen
	glEnable(GL_DEPTH_TEST);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glDisable(GL_CULL_FACE);
	
	m_proceduralShader.bind(); 
	
	// set parameters
	m_proceduralShader.setMatrix4x4Uniform("worldcamera", m_camera.getTransformation().Inverse());
	m_proceduralShader.setMatrix4x4Uniform("projection", m_camera.getProjectionMatrix());
	m_proceduralShader.setMatrix4x4Uniform("modelworld", m_mesh.getTransformation() );
	
	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_NORMAL_ARRAY);
	
	glVertexPointer( 3, GL_DOUBLE, 0, m_mesh.getVertexPointer() );
	glNormalPointer( GL_DOUBLE, 0, m_mesh.getNormalPointer() );
	
	fprintf(stderr, "m_mesh.getNumberOfParts(): %d\n", m_mesh.getNumberOfParts());
	for(unsigned int i = 0; i < m_mesh.getNumberOfParts(); i++)
	{
		m_permutationTable.setLayer(0);
		m_permutationTable.bind();
		m_gradient2DTable.setLayer(1);
		m_gradient3DTable.setLayer(1);
		m_gradient4DTable.setLayer(1);
		m_gradient3DTable.bind();
		m_colorMap.setLayer(2);
		m_colorMap.bind();
		m_proceduralShader.setIntUniform("permSampler", m_permutationTable.getLayer());
		m_proceduralShader.setIntUniform("gradSampler3d", m_gradient3DTable.getLayer());
		m_proceduralShader.setIntUniform("colorMapSampler", m_colorMap.getLayer());
		
		m_proceduralShader.setVector3Uniform("diffuseColor", 
										m_mesh.getMaterial(i).m_diffuseColor.x, 
										m_mesh.getMaterial(i).m_diffuseColor.y, 
										m_mesh.getMaterial(i).m_diffuseColor.z );
		m_proceduralShader.setFloatUniform("specularExp", m_mesh.getMaterial(i).m_specularExp);
		glDrawElements( GL_TRIANGLES, m_mesh.getNumberOfFaces(i)*3, GL_UNSIGNED_INT, m_mesh.getVertexIndicesPointer(i) );
		m_permutationTable.unbind();
		m_gradient3DTable.unbind();
		
	}
	
	glDisableClientState(GL_NORMAL_ARRAY);
	glDisableClientState(GL_VERTEX_ARRAY);
	
	
	m_proceduralShader.unbind();
		
}


//-----------------------------------------------------------------------------


std::vector<float>
ProceduralViewer::
createColorMapTexture( int mapSize, const Vector3 * discreteColorMap, int outputTextureSize )
{
	// calculate spline from discrete color map to create a color texture
	std::vector<float> colorMapTexture;
    
    // Exercise 5.4
	
	for(int i = 0; i < outputTextureSize; i++) {
		Vector3 color = Vector3(0.0,0.0,0.0);
		colorMapTexture.push_back(  color.x );
		colorMapTexture.push_back(  color.y );
		colorMapTexture.push_back(  color.z );
	}
	return colorMapTexture;
}




//=============================================================================
